home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2007 December / PCWKCD1207B.iso / Blogowanie poza sfera / Flock 1.0 beta / flock-1.0RC3.en-US.win32.exe / flock / components / flockIndexer.js < prev    next >
Text File  |  2007-10-18  |  22KB  |  678 lines

  1. // BEGIN FLOCK GPL
  2. // 
  3. // Copyright Flock Inc. 2005-2007
  4. // http://flock.com
  5. // 
  6. // This file may be used under the terms of of the
  7. // GNU General Public License Version 2 or later (the "GPL"),
  8. // http://www.gnu.org/licenses/gpl.html
  9. // 
  10. // Software distributed under the License is distributed on an "AS IS" basis,
  11. // WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  12. // for the specific language governing rights and limitations under the
  13. // License.
  14. // 
  15. // END FLOCK GPL
  16.  
  17. const CLASS_ID                = Components.ID("{C2BFF231-7A51-4764-8409-A1B22B2F5147}");
  18. const CLASS_NAME              = "Flock Indexer";
  19. const CONTRACT_ID             = "@flock.com/indexer;1";
  20.  
  21. const FLOCK_NS = "http://flock.com/rdf#";
  22. const NSCP_NS = "http://home.netscape.com/NC-rdf#";
  23.  
  24. const PROP_URL   = NSCP_NS + "URL";
  25. const PROP_NAME  = NSCP_NS + "Name";
  26. const PROP_CHILD = NSCP_NS + "child";
  27.  
  28. const Cc = Components.classes;
  29. const Ci = Components.interfaces;
  30. const Cr = Components.results;
  31.  
  32. /* from nspr's prio.h */
  33. const PR_RDONLY      = 0x01;
  34. const PR_WRONLY      = 0x02;
  35. const PR_RDWR        = 0x04;
  36. const PR_CREATE_FILE = 0x08;
  37. const PR_APPEND      = 0x10;
  38. const PR_TRUNCATE    = 0x20;
  39. const PR_SYNC        = 0x40;
  40. const PR_EXCL        = 0x80;
  41.  
  42. const OP_DELETE      = 0;
  43. const OP_ADD_FLOCK   = 1;
  44. const OP_ADD_HISTORY = 2;
  45.  
  46. function flockIndexer() {
  47.   var obs = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
  48.   obs.addObserver(this, "xpcom-shutdown", false);
  49. }
  50.  
  51. flockIndexer.prototype = {
  52.  
  53.   init: function INDEXER_init() {
  54.     this._enabled = false;
  55.     this._enabledWebHistory = false;
  56.     
  57.     this._logger = Cc["@flock.com/logger;1"].createInstance(Ci.flockILogger);
  58.     this._logger.init("indexer");
  59.     
  60.     this._logger.info("starting up...");
  61.     
  62.     this._profiler = Cc["@flock.com/profiler;1"].getService(Ci.flockIProfiler);
  63.  
  64.     this._RDFS = Cc["@mozilla.org/rdf/rdf-service;1"]
  65.       .getService(Ci.nsIRDFService);
  66.     this._resURL = this._RDFS.GetResource(PROP_URL);
  67.     this._resName = this._RDFS.GetResource(PROP_NAME);
  68.     this._resHistoryRoot = this._RDFS.GetResource("NC:HistoryRoot");
  69.     this._resHistoryChild = this._RDFS.GetResource(PROP_CHILD);
  70.     this._resIsIndexable = this._RDFS.GetResource(FLOCK_NS + "isIndexable");
  71.     this._resTitle = this._RDFS.GetResource(NSCP_NS + "Name");
  72.     this._resDescription = this._RDFS.GetResource(NSCP_NS + "Description");
  73.     this._resTags = this._RDFS.GetResource(FLOCK_NS + "tags");
  74.  
  75.     this._searchService = Cc["@flock.com/lucene/flockLucene;1"]
  76.       .getService(Ci.flockILucene);
  77.     this._favService = Cc["@mozilla.org/rdf/datasource;1?name=flock-favorites"]
  78.       .getService(Ci.flockIRDFObservable);
  79.     this._historyRdf = Cc["@mozilla.org/rdf/datasource;1?name=history"]
  80.       .getService(Ci.nsIRDFDataSource);
  81.     this._ios = Cc["@mozilla.org/network/io-service;1"]
  82.       .getService(Ci.nsIIOService);
  83.  
  84.     this._coop = Cc["@flock.com/singleton;1"]
  85.                  .getService(Ci.flockISingleton)
  86.                  .getSingleton("chrome://flock/content/common/load-faves-coop.js")
  87.                  .wrappedJSObject;
  88.  
  89.     var dirService = Cc["@mozilla.org/file/directory_service;1"]
  90.       .getService(Ci.nsIProperties);
  91.     var profileDir = dirService.get("ProfD", Ci.nsIFile);
  92.  
  93.     // Clean up old cardinal index dir
  94.     var oldHistorySearchDir = profileDir.clone();
  95.     oldHistorySearchDir.append("historysearch");
  96.     try {
  97.       oldHistorySearchDir.remove(true);
  98.     }
  99.     catch (ex) {
  100.     }
  101.  
  102.     // initialize Lucene
  103.     var luceneDir = profileDir.clone();
  104.     luceneDir.append("lucene");
  105.     this._searchService.init(luceneDir);
  106.  
  107.     // initialize the queue
  108.     this._initQueue();
  109.  
  110.     // init queue processing timer
  111.     this._timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
  112.     
  113.     // init page text store
  114.     this._pageText = {};
  115.     
  116.     // create tokenizer
  117.     this._tokenizer = Cc["@flock.com/tokenizer;1"].createInstance(Ci.flockITokenizer);
  118.     
  119.     // init with current prefs
  120.     this.observe(null, "nsPref:changed", null);
  121.     
  122.     // watch for pref changes
  123.     var prefService = Cc["@mozilla.org/preferences-service;1"]
  124.       .getService(Ci.nsIPrefBranch2);
  125.     prefService.addObserver("flock.service.indexer.enabled", this, false);
  126.     prefService.addObserver("flock.service.indexer.indexWebHistory", this, false);
  127.   },
  128.   
  129.   rebuildIndex: function INDEXER_rebuildIndex() {
  130.     this._logger.info("rebuilding index...");
  131.  
  132.     // reindex history
  133.     var records = this._historyRdf.GetTargets(this._resHistoryRoot,
  134.                                               this._resHistoryChild,
  135.                                               true);
  136.     var record, url;
  137.     while (records.hasMoreElements()) {
  138.       record = records.getNext().QueryInterface(Ci.nsIRDFResource);
  139.       try {
  140.         url = this._ios.newURI(record.ValueUTF8, null, null);
  141.       } catch (e) {
  142.         url = null;
  143.       }
  144.       if (url && url.scheme == "http") {
  145.         this._addOp(OP_ADD_HISTORY, "history:" + url.spec);
  146.       }
  147.     }
  148.   },
  149.   
  150.   _getQueueFile: function INDEXER__getQueueFile() {
  151.     var file = Cc["@mozilla.org/file/directory_service;1"]
  152.       .getService(Ci.nsIProperties).get("ProfD", Ci.nsILocalFile);
  153.     file.append("indexerQueue.js");
  154.     return file;
  155.   },
  156.   
  157.   _initQueue: function INDEXER__initQueue() {
  158.     var queue;
  159.  
  160.     try {
  161.       var file = this._getQueueFile();
  162.  
  163.       var stream = Cc["@mozilla.org/network/file-input-stream;1"]
  164.         .createInstance(Ci.nsIFileInputStream);
  165.       stream.init(file, PR_RDONLY, 0, 0);
  166.  
  167.       var cvstream = Cc["@mozilla.org/intl/converter-input-stream;1"]
  168.         .createInstance(Ci.nsIConverterInputStream);
  169.       cvstream.init(stream, "UTF-8", 1024,
  170.                     Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
  171.  
  172.       var content = "";
  173.       var data = {};
  174.       while (cvstream.readString(4096, data)) {
  175.         content += data.value;
  176.       }
  177.       cvstream.close();
  178.  
  179.       queue = content.replace(/\r\n?/g, "\n");
  180.       file.remove(false);
  181.     } catch (e) {
  182.       queue = null;
  183.     }
  184.  
  185.     if (queue) {
  186.       try {
  187.         var s = new Components.utils.Sandbox("about:blank");
  188.         this._queue = Components.utils.evalInSandbox(queue, s);
  189.         this._logger.info("restored queue");
  190.         return;
  191.       } catch (e) {
  192.         this._logger.error("unable to restore queue");
  193.       }
  194.     }
  195.     
  196.     // default to empty queue
  197.     this._queue = [];
  198.   },
  199.   
  200.   _saveQueue: function INDEXER__saveQueue() {
  201.     // do nothing if queue is empty
  202.     if (this._queue.length == 0)
  203.       return;
  204.     
  205.     try {
  206.       this._logger.info("saving queue...");
  207.       var file = this._getQueueFile();
  208.  
  209.       var ostream = Cc["@mozilla.org/network/safe-file-output-stream;1"]
  210.         .createInstance(Ci.nsIFileOutputStream);
  211.       ostream.init(file, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, 0600, 0);
  212.  
  213.       var converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
  214.         .createInstance(Ci.nsIScriptableUnicodeConverter);
  215.       converter.charset = "UTF-8";
  216.  
  217.       var data = this._queue.toSource();
  218.       var convdata = converter.ConvertFromUnicode(data) + converter.Finish();
  219.  
  220.       ostream.write(convdata, convdata.length);
  221.  
  222.       if (ostream instanceof Ci.nsISafeOutputStream) {
  223.         ostream.finish();
  224.       } else {
  225.         ostream.close();
  226.       }
  227.     } catch (e) {
  228.       this._logger.error("unable to save queue");
  229.     }
  230.   },
  231.   
  232.   _enable: function INDEXER__enable() {
  233.     if (!this._enabled) {
  234.       // start watching the favorites and history graphs
  235.       this._favService.addArcObserver(Ci.flockIRDFObserver.TYPE_ALL, null,
  236.                                       this._resIsIndexable, null, this);
  237.       this._favService.addArcObserver(Ci.flockIRDFObserver.TYPE_CHANGE, null,
  238.                                       this._resTitle, null, this);
  239.       this._favService.addArcObserver(Ci.flockIRDFObserver.TYPE_CHANGE, null,
  240.                                       this._resDescription, null, this);
  241.       this._favService.addArcObserver(Ci.flockIRDFObserver.TYPE_CHANGE, null,
  242.                                       this._resTags, null, this);
  243.       this._historyRdf.AddObserver(this);
  244.       this._enabled = true;
  245.       this._enabledWebHistory = true;
  246.       this._logger.info("indexing enabled");
  247.     }
  248.   },
  249.   
  250.   _disable: function INDEXER__disable() {
  251.     if (this._enabled) {
  252.       // stop watching the favorites and history graphs
  253.       this._favService.removeArcObserver(Ci.flockIRDFObserver.TYPE_ALL, null,
  254.                                          this._resIsIndexable, null, this);
  255.       this._favService.removeArcObserver(Ci.flockIRDFObserver.TYPE_CHANGE, null,
  256.                                       this._resTitle, null, this);
  257.       this._favService.removeArcObserver(Ci.flockIRDFObserver.TYPE_CHANGE, null,
  258.                                       this._resDescription, null, this);
  259.       this._favService.removeArcObserver(Ci.flockIRDFObserver.TYPE_CHANGE, null,
  260.                                       this._resTags, null, this);
  261.       this._historyRdf.RemoveObserver(this);
  262.       this._enabled = false;
  263.       this._enabledWebHistory = false;
  264.       this._logger.info("indexing disabled");
  265.     }
  266.   },
  267.  
  268.   _enableWebHistory: function INDEXER__enableWebHistory() {
  269.     if (this._enabled) {
  270.       if (!this._enabledWebHistory) {
  271.         this._historyRdf.AddObserver(this);
  272.         this._enabledWebHistory = true;
  273.         this._logger.info("web history indexing enabled");
  274.       }
  275.     }
  276.   },
  277.   
  278.   _disableWebHistory: function INDEXER__disableWebHistory() {
  279.     if (this._enabled) {
  280.       if (this._enabledWebHistory) {
  281.         this._historyRdf.RemoveObserver(this);
  282.         this._enabledWebHistory = false;
  283.         this._logger.info("web history indexing disabled");
  284.       }
  285.     }
  286.   },
  287.   
  288.   _shutdown: function INDEXER__shutdown() {
  289.     this._disable();
  290.     this._timer.cancel();
  291.     this._saveQueue();
  292.     this._logger.info("shut down");
  293.   },
  294.   
  295.   _processQueue: function INDEXER__processQueue(sync) {
  296.     var batchSize = 1;
  297.     var c = 0;
  298.     while (this._queue.length > 0 && c < batchSize) {
  299.       c++;
  300.       var op = this._queue[0];
  301.       var instruction = op[0];
  302.       var uri = op[1];
  303.       switch (instruction) {
  304.         case OP_DELETE:
  305.           this._logger.info("removing " + uri);
  306.           if (sync) {
  307.             this._searchService.deleteDocumentSync(uri);
  308.           } else {
  309.             this._searchService.deleteDocument(uri, this);
  310.           }
  311.           break;
  312.  
  313.         case OP_ADD_FLOCK:
  314.           var obj = this._coop.get(uri);
  315.           if (!obj) {
  316.             var msg = "trying to add nonexistent object: " + uri;
  317.             this._logger.error(msg);
  318.             throw Components.Exception(msg, Cr.NS_ERROR_UNEXPECTED);
  319.           }
  320.  
  321.           var url = obj.URL;
  322.           if (!url) {
  323.              this._logger.warn("unable to get URL for " + uri);
  324.              url = "";
  325.           }
  326.  
  327.           var type = obj.flockType;
  328.           var title = obj.name;
  329.           if (obj.tags) {
  330.             var tags = obj.tags;
  331.           } else {
  332.             var tags = "";
  333.           }
  334.           if (obj.description) {
  335.             var description = obj.description;
  336.           } else {
  337.             var description = "";
  338.           }
  339.           var text = "";
  340.           this._logger.info("indexing " + uri + " type: " + type + " title: " + title + " url: " + url);
  341.  
  342.           if (sync) {
  343.             this._searchService.addDocumentSync(uri, type, url, title, tags, description, text);
  344.           } else {
  345.             this._searchService.addDocument(uri, type, url, title, tags, description, text, this);
  346.           }
  347.           break;
  348.  
  349.         case OP_ADD_HISTORY:
  350.           this._logger.info("indexing " + uri + " type: history");
  351.           if (!sync) {
  352.             this._addHistoryDocument(uri);
  353.           }
  354.           break;
  355.       }
  356.     }
  357.   },
  358.   
  359.   _addHistoryDocument: function INDEXER__addHistoryDocument(aURI) {
  360.     var uri = aURI;
  361.     var url = uri.substr("history:".length);
  362.     var title = "";
  363.     var tags = "";
  364.     var description = "";
  365.     var data = "";
  366.     
  367.     var resource = this._historyRdf.GetSource(this._resURL,
  368.                                               this._RDFS.GetResource(url),
  369.                                               true);
  370.     if (resource) {
  371.       var title_node = this._historyRdf.GetTarget(resource, this._resName, true);
  372.       if (title_node && title_node.QueryInterface(Ci.nsIRDFLiteral)) {
  373.         title = title_node.Value;
  374.         this._logger.debug("got title");
  375.       }
  376.     }
  377.     if (url in this._pageText) {
  378.       data = this._pageText[url];
  379.       delete this._pageText[url];
  380.     }
  381.     this._searchService.addDocument(uri, "history", url, title, tags, description, data, this);
  382.     this._logger.debug("document indexed text: " + data);
  383.   },
  384.   
  385.   _addOp: function INDEXER__addOp(aOp, aURI) {
  386.     this._queue.push([aOp, aURI]);
  387.     this._logger.info("queued operation op: " + aOp + " uri: " + aURI);
  388.     this._timer.initWithCallback(this, 250, Ci.nsITimer.TYPE_ONE_SHOT);
  389.   },
  390.   
  391.   _retireOp: function INDEXER__retireOp() {
  392.     this._queue.shift();
  393.     if (this._queue.length > 0) {
  394.       this._timer.initWithCallback(this, 0, Ci.nsITimer.TYPE_ONE_SHOT);
  395.     }
  396.   },
  397.   
  398.   _handlePageLoad: function INDEXER_handlePageLoad(document) {
  399.     var profilerEvt = this._profiler.profileEventStart("indexer-tokenize");
  400.     var url = document.documentURI;
  401.     this._logger.debug("_handlePageLoad: " + url);
  402.     if (document.body) {
  403.       this._pageText[url] = this._tokenizer.tokenizeDOMNode(document,
  404.                                                             document.body);
  405.     }
  406.     this._addOp(OP_ADD_HISTORY, "history:" + url);
  407.     this._profiler.profileEventEnd(profilerEvt, url);
  408.   },
  409.   
  410.   // nsIDOMEventListener
  411.   handleEvent: function INDEXER_handleEvent(aEvent) {
  412.     switch(aEvent.type) {
  413.       case "load":
  414.         var url;
  415.         try {
  416.           url = this._ios.newURI(aEvent.originalTarget.documentURI, null, null);
  417.         } catch (e) { }
  418.         if (url && url.scheme == "http") {
  419.           this._handlePageLoad(aEvent.originalTarget);
  420.         }
  421.         break;
  422.     }
  423.   },
  424.  
  425.   // flockIMigratable
  426.   get migrationName() { return "Indexer"; },
  427.   
  428.   // flockIMigratable
  429.   needsMigration: function INDEXER_needsMigration(oldVersion) {
  430.     return oldVersion.substr(0, 3) == "0.7";
  431.   },
  432.   
  433.   // flockIMigratable
  434.   startMigration: function INDEXER_startMigration(oldVersion, listener) {
  435.     return null;
  436.   },
  437.   
  438.   // flockIMigratable
  439.   finishMigration: function INDEXER_finishMigration(ctxtWrapper) {
  440.   },
  441.   
  442.   // flockIMigratable
  443.   doMigrationWork: function INDEXER_doMigrationWork(ctxtWrapper) {
  444.     this.rebuildIndex();
  445.     return false;
  446.   },
  447.   
  448.   // flockILuceneListener
  449.   onAddDocumentComplete: function INDEXER_onAddDocumentComplete(aURI) {
  450.     this._logger.debug("onAddDocumentComplete: " + aURI);
  451.     this._retireOp();
  452.   },
  453.  
  454.   // flockILuceneListener
  455.   onDeleteDocumentComplete: function INDEXER_onDeleteDocumentComplete(aURI) {
  456.     this._logger.debug("onDeleteDocumentComplete: " + aURI);
  457.     this._retireOp();
  458.   },
  459.  
  460.   // nsITimerCallback
  461.   notify: function INDEXER_notify(timer) {
  462.     this._processQueue(false);
  463.   },
  464.  
  465.   // flockIRDFObserver
  466.   rdfChanged: function INDEXER__rdfChanged(ds, type, rsrc, pred, obj, oldObj) {
  467.     if (pred == this._resIsIndexable) {
  468.       switch (type) {
  469.         case Ci.flockIRDFObserver.TYPE_ASSERT:
  470.           var indexable = this._coop.get_from_resource(rsrc);
  471.           if (indexable.isIndexable) {
  472.             this._addOp(OP_ADD_FLOCK, indexable.id());
  473.           }
  474.           break;
  475.   
  476.         case Ci.flockIRDFObserver.TYPE_CHANGE:
  477.           var indexable = this._coop.get_from_resource(rsrc);
  478.           var op = indexable.isIndexable ? OP_ADD_FLOCK : OP_DELETE;
  479.           this._addOp(op, indexable.id());
  480.           break;
  481.   
  482.         case Ci.flockIRDFObserver.TYPE_UNASSERT:
  483.           rsrc.QueryInterface(Ci.nsIRDFResource);
  484.           this._addOp(OP_DELETE, rsrc.ValueUTF8);
  485.           break;
  486.       }
  487.     } else {
  488.       var indexable = this._coop.get_from_resource(rsrc);
  489.       if (indexable && indexable.isIndexable) {
  490.         this._addOp(OP_ADD_FLOCK, indexable.id());
  491.       }
  492.     }
  493.   },
  494.  
  495.   // nsIRDFObserver
  496.   onAssert: function INDEXER_onAssert(ds, source, predicate, target) {
  497.     if (predicate.ValueUTF8 == PROP_NAME &&
  498.         ds.HasAssertion(this._resHistoryRoot, this._resHistoryChild,
  499.                         source, true)) {
  500.       var url = null;
  501.       try {
  502.         url = this._ios.newURI(source.ValueUTF8, null, null);
  503.       }
  504.       catch (e) { }
  505.       if (url && url.scheme == "http") {
  506.         this._addOp(OP_ADD_HISTORY, "history:" + url.spec);
  507.       }
  508.     }
  509.   },
  510.   
  511.   // nsIRDFObserver
  512.   onUnassert: function INDEXER_onUnassert(ds, source, predicate, target) {
  513.     if (source.ValueUTF8 == "NC:HistoryRoot" &&
  514.         predicate.ValueUTF8 == PROP_CHILD) {
  515.       target.QueryInterface(Ci.nsIRDFResource);
  516.       var url = null;
  517.       try {
  518.         url = this._ios.newURI(target.ValueUTF8, null, null);
  519.       } catch (e) {}
  520.       if (url && url.scheme == "http") {
  521.         this._addOp(OP_DELETE, "history:" + url.spec);
  522.       }
  523.     }
  524.   },
  525.   
  526.   // nsIRDFObserver
  527.   onChange: function INDEXER_onChange(ds, source, predicate, oldTarget, newTarget) {
  528.     if (predicate.ValueUTF8 == PROP_NAME &&
  529.         ds.HasAssertion(this._resHistoryRoot, this._resHistoryChild,
  530.                         source, true)) {
  531.       var url = null;
  532.       try {
  533.         url = this._ios.newURI(source.ValueUTF8, null, null);
  534.       } catch (e) {}
  535.       if (url && url.scheme == "http") {
  536.         this._addOp(OP_ADD_HISTORY, "history:" + url.spec);
  537.       }
  538.     }
  539.   },
  540.   
  541.   // nsIRDFObserver
  542.   onMove: function INDEXER_onMove(ds, oldSource, newSource, predicate, target) {
  543.   },
  544.   
  545.   // nsIRDFObserver
  546.   onBeginUpdateBatch: function INDEXER_onBeginUpdateBatch(ds) {
  547.   },
  548.   
  549.   // nsIRDFObserver
  550.   onEndUpdateBatch: function INDEXER_onEndUpdateBatch(ds) {
  551.   },
  552.  
  553.   // nsIObserver
  554.   observe: function INDEXER_observe(subject, topic, state) {
  555.     switch (topic) {
  556.       case "xpcom-shutdown":
  557.         var obs = Cc["@mozilla.org/observer-service;1"]
  558.           .getService(Ci.nsIObserverService);
  559.         obs.removeObserver(this, "xpcom-shutdown");
  560.         this._shutdown();
  561.         return;
  562.       case "nsPref:changed":
  563.         var prefService = Cc["@mozilla.org/preferences-service;1"]
  564.           .getService(Ci.nsIPrefBranch);
  565.         if (prefService.getPrefType("flock.service.indexer.enabled")) {
  566.           if (prefService.getBoolPref("flock.service.indexer.enabled")) {
  567.             this._enable();
  568.           } else {
  569.             this._disable();
  570.           }
  571.         } else {
  572.           this._enable();
  573.         }
  574.         if (prefService.getPrefType("flock.service.indexer.indexWebHistory")) {
  575.           if (prefService.getBoolPref("flock.service.indexer.indexWebHistory")) {
  576.             this._enableWebHistory();
  577.           } else {
  578.             this._disableWebHistory();
  579.           }
  580.         } else {
  581.           this._enableWebHistory();
  582.         }
  583.         break;
  584.     }
  585.   },
  586.   
  587.   // nsIClassInfo
  588.   getInterfaces: function INDEXER_getInterfaces(aCount) {
  589.     var interfaces = [Ci.flockIIndexer, Ci.nsIClassInfo, Ci.nsIObserver,
  590.                       Ci.flockIRDFObserver, Ci.nsIRDFObserver,
  591.                       Ci.nsITimerCallback, Ci.flockILuceneListener,
  592.                       Ci.flockIMigratable, Ci.nsIDOMEventListener];
  593.     aCount.value = interfaces.length;
  594.     return interfaces;
  595.   },
  596.  
  597.   // nsIClassInfo
  598.   getHelperForLanguage: function INDEXER_getHelperForLanguage(aLanguage) {
  599.     return null;
  600.   },
  601.  
  602.   // nsIClassInfo
  603.   contractID: CONTRACT_ID,
  604.  
  605.   // nsIClassInfo
  606.   classDescription: CLASS_NAME,
  607.  
  608.   // nsIClassInfo
  609.   classID: CLASS_ID,
  610.  
  611.   // nsIClassInfo
  612.   implementationLanguage: Ci.nsIProgrammingLanguage.JAVASCRIPT,
  613.  
  614.   // nsIClassInfo
  615.   flags: Ci.nsIClassInfo.SINGLETON,
  616.   
  617.   // nsISupports
  618.   QueryInterface: function INDEXER_QueryInterface(aIID) {
  619.     if (!aIID.equals(Ci.nsISupports) &&
  620.         !aIID.equals(Ci.flockIIndexer) &&
  621.         !aIID.equals(Ci.nsIClassInfo) &&
  622.         !aIID.equals(Ci.nsIObserver) &&
  623.         !aIID.equals(Ci.flockIRDFObserver) &&
  624.         !aIID.equals(Ci.nsIRDFObserver) &&
  625.         !aIID.equals(Ci.nsITimerCallback) &&
  626.         !aIID.equals(Ci.flockILuceneListener) &&
  627.         !aIID.equals(Ci.flockIMigratable) &&
  628.         !aIID.equals(Ci.nsIDOMEventListener))
  629.       throw Cr.NS_ERROR_NO_INTERFACE;
  630.     return this;
  631.   }
  632.  
  633. };
  634.  
  635. /******************************************************************************
  636.  * XPCOM Functions for construction and registration
  637.  ******************************************************************************/
  638. var Module = {
  639.   _firstTime: true,
  640.   registerSelf: function(aCompMgr, aFileSpec, aLocation, aType) {
  641.     if (this._firstTime) {
  642.       this._firstTime = false;
  643.       throw Cr.NS_ERROR_FACTORY_REGISTER_AGAIN;
  644.     }
  645.     aCompMgr = aCompMgr.QueryInterface(Ci.nsIComponentRegistrar);
  646.     aCompMgr.registerFactoryLocation(CLASS_ID, CLASS_NAME, CONTRACT_ID, aFileSpec, aLocation, aType);
  647.     
  648.     var catman = Cc["@mozilla.org/categorymanager;1"].getService(Ci.nsICategoryManager);
  649.     catman.addCategoryEntry("flockMigratable", CLASS_NAME, CONTRACT_ID, true, true);
  650.   },
  651.  
  652.   unregisterSelf: function(aCompMgr, aLocation, aType) {
  653.     aCompMgr = aCompMgr.QueryInterface(Ci.nsIComponentRegistrar);
  654.     aCompMgr.unregisterFactoryLocation(CLASS_ID, aLocation);        
  655.   },
  656.   
  657.   getClassObject: function(aCompMgr, aCID, aIID) {
  658.     if (!aIID.equals(Ci.nsIFactory))
  659.       throw Cr.NS_ERROR_NOT_IMPLEMENTED;
  660.     if (aCID.equals(CLASS_ID))
  661.       return Factory;
  662.     throw Cr.NS_ERROR_NO_INTERFACE;
  663.   },
  664.  
  665.   canUnload: function(aCompMgr) { return true; }
  666. };
  667.  
  668. var Factory = {
  669.   createInstance: function(aOuter, aIID)
  670.   {
  671.     if (aOuter != null)
  672.       throw Cr.NS_ERROR_NO_AGGREGATION;
  673.     return (new flockIndexer()).QueryInterface(aIID);
  674.   }
  675. };
  676.  
  677. function NSGetModule(aCompMgr, aFileSpec) { return Module; }
  678.